#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include "shell.h"
#include "input.h"
#include "file.h"
#include "sound.h"

#define USE_SAVE_STATE  0

#if USE_SAVE_STATE
#define STATE_MAX 0x2000
static uint8_t state[STATE_MAX] = {0}; /* savestate data */
#endif //USE_SAVE_STATE

#define _1802TRACE 0

/* COSMAC VIP operating system, located at $8000 (ROM) */
static const uint8_t vip_os[0x200] = {
    0xf8, 0x80, 0xb2, 0xf8, 0x08, 0xa2, 0xe2, 0xd2, 0x64, 0x00, 0x62, 0x0c, 0xf8, 0xff, 0xa1, 0xf8,
    0x0f, 0xb1, 0xf8, 0xaa, 0x51, 0x01, 0xfb, 0xaa, 0x32, 0x22, 0x91, 0xff, 0x04, 0x3b, 0x22, 0xb1,
    0x30, 0x12, 0x36, 0x28, 0x90, 0xa0, 0xe0, 0xd0, 0xe1, 0xf8, 0x00, 0x73, 0x81, 0xfb, 0xaf, 0x3a,
    0x29, 0xf8, 0xd2, 0x73, 0xf8, 0x9f, 0x51, 0x81, 0xa0, 0x91, 0xb0, 0xf8, 0xcf, 0xa1, 0xd0, 0x73,
    0x20, 0x20, 0x40, 0xff, 0x01, 0x20, 0x50, 0xfb, 0x82, 0x3a, 0x3e, 0x92, 0xb3, 0xf8, 0x51, 0xa3,
    0xd3, 0x90, 0xb2, 0xbb, 0xbd, 0xf8, 0x81, 0xb1, 0xb4, 0xb5, 0xb7, 0xba, 0xbc, 0xf8, 0x46, 0xa1,
    0xf8, 0xaf, 0xa2, 0xf8, 0xdd, 0xa4, 0xf8, 0xc6, 0xa5, 0xf8, 0xba, 0xa7, 0xf8, 0xa1, 0xac, 0xe2,
    0x69, 0xdc, 0xd7, 0xd7, 0xd7, 0xb6, 0xd7, 0xd7, 0xd7, 0xa6, 0xd4, 0xdc, 0xbe, 0x32, 0xf4, 0xfb,
    0x0a, 0x32, 0xef, 0xdc, 0xae, 0x22, 0x61, 0x9e, 0xfb, 0x0b, 0x32, 0xc2, 0x9e, 0xfb, 0x0f, 0x3a,
    0x8f, 0xf8, 0x6f, 0xac, 0xf8, 0x40, 0xb9, 0x93, 0xf6, 0xdc, 0x29, 0x99, 0x3a, 0x97, 0xf8, 0x10,
    0xa7, 0xf8, 0x08, 0xa9, 0x46, 0xb7, 0x93, 0xfe, 0xdc, 0x86, 0x3a, 0xad, 0x2e, 0x97, 0xf6, 0xb7,
    0xdc, 0x29, 0x89, 0x3a, 0xad, 0x17, 0x87, 0xf6, 0xdc, 0x8e, 0x3a, 0x9e, 0xdc, 0x69, 0x26, 0xd4,
    0x30, 0xc0, 0xf8, 0x83, 0xac, 0xf8, 0x0a, 0xb9, 0xdc, 0x33, 0xc5, 0x29, 0x99, 0x3a, 0xc8, 0xdc,
    0x3b, 0xcf, 0xf8, 0x09, 0xa9, 0xa7, 0x97, 0x76, 0xb7, 0x29, 0xdc, 0x89, 0x3a, 0xd6, 0x87, 0xf6,
    0x33, 0xe3, 0x7b, 0x97, 0x56, 0x16, 0x86, 0x3a, 0xcf, 0x2e, 0x8e, 0x3a, 0xcf, 0x30, 0xbd, 0xdc,
    0x16, 0xd4, 0x30, 0xef, 0xd7, 0xd7, 0xd7, 0x56, 0xd4, 0x16, 0x30, 0xf4, 0x00, 0x00, 0x00, 0x00,
    0x30, 0x39, 0x22, 0x2a, 0x3e, 0x20, 0x24, 0x34, 0x26, 0x28, 0x2e, 0x18, 0x14, 0x1c, 0x10, 0x12,
    0xf0, 0x80, 0xf0, 0x80, 0xf0, 0x80, 0x80, 0x80, 0xf0, 0x50, 0x70, 0x50, 0xf0, 0x50, 0x50, 0x50,
    0xf0, 0x80, 0xf0, 0x10, 0xf0, 0x80, 0xf0, 0x90, 0xf0, 0x90, 0xf0, 0x10, 0xf0, 0x10, 0xf0, 0x90,
    0xf0, 0x90, 0x90, 0x90, 0xf0, 0x10, 0x10, 0x10, 0x10, 0x60, 0x20, 0x20, 0x20, 0x70, 0xa0, 0xa0,
    0xf0, 0x20, 0x20, 0x7a, 0x42, 0x70, 0x22, 0x78, 0x22, 0x52, 0xc4, 0x19, 0xf8, 0x00, 0xa0, 0x9b,
    0xb0, 0xe2, 0xe2, 0x80, 0xe2, 0xe2, 0x20, 0xa0, 0xe2, 0x20, 0xa0, 0xe2, 0x20, 0xa0, 0x3c, 0x53,
    0x98, 0x32, 0x67, 0xab, 0x2b, 0x8b, 0xb8, 0x88, 0x32, 0x43, 0x7b, 0x28, 0x30, 0x44, 0xd3, 0xf8,
    0x0a, 0x3b, 0x76, 0xf8, 0x20, 0x17, 0x7b, 0xbf, 0xff, 0x01, 0x3a, 0x78, 0x39, 0x6e, 0x7a, 0x9f,
    0x30, 0x78, 0xd3, 0xf8, 0x10, 0x3d, 0x85, 0x3d, 0x8f, 0xff, 0x01, 0x3a, 0x87, 0x17, 0x9c, 0xfe,
    0x35, 0x90, 0x30, 0x82, 0xd3, 0xe2, 0x9c, 0xaf, 0x2f, 0x22, 0x8f, 0x52, 0x62, 0xe2, 0xe2, 0x3e,
    0x98, 0xf8, 0x04, 0xa8, 0x88, 0x3a, 0xa4, 0xf8, 0x04, 0xa8, 0x36, 0xa7, 0x88, 0x31, 0xaa, 0x8f,
    0xfa, 0x0f, 0x52, 0x30, 0x94, 0x00, 0x00, 0x00, 0x00, 0xd3, 0xdc, 0xfe, 0xfe, 0xfe, 0xfe, 0xae,
    0xdc, 0x8e, 0xf1, 0x30, 0xb9, 0xd4, 0xaa, 0x0a, 0xaa, 0xf8, 0x05, 0xaf, 0x4a, 0x5d, 0x8d, 0xfc,
    0x08, 0xad, 0x2f, 0x8f, 0x3a, 0xcc, 0x8d, 0xfc, 0xd9, 0xad, 0x30, 0xc5, 0xd3, 0x22, 0x06, 0x73,
    0x86, 0x73, 0x96, 0x52, 0xf8, 0x06, 0xae, 0xf8, 0xd8, 0xad, 0x02, 0xf6, 0xf6, 0xf6, 0xf6, 0xd5,
    0x42, 0xfa, 0x0f, 0xd5, 0x8e, 0xf6, 0xae, 0x32, 0xdc, 0x3b, 0xea, 0x1d, 0x1d, 0x30, 0xea, 0x01
};

/* CHIP-8 interpreter, loaded into RAM at $0000 */
static const uint8_t chip8_int[0x200] = {
    0x91, 0xbb, 0xff, 0x01, 0xb2, 0xb6, 0xf8, 0xcf, 0xa2, 0xf8, 0x81, 0xb1, 0xf8, 0x46, 0xa1, 0x90,
    0xb4, 0xf8, 0x1b, 0xa4, 0xf8, 0x01, 0xb5, 0xf8, 0xfc, 0xa5, 0xd4, 0x96, 0xb7, 0xe2, 0x94, 0xbc,
    0x45, 0xaf, 0xf6, 0xf6, 0xf6, 0xf6, 0x32, 0x44, 0xf9, 0x50, 0xac, 0x8f, 0xfa, 0x0f, 0xf9, 0xf0,
    0xa6, 0x05, 0xf6, 0xf6, 0xf6, 0xf6, 0xf9, 0xf0, 0xa7, 0x4c, 0xb3, 0x8c, 0xfc, 0x0f, 0xac, 0x0c,
    0xa3, 0xd3, 0x30, 0x1b, 0x8f, 0xfa, 0x0f, 0xb3, 0x45, 0x30, 0x40, 0x22, 0x69, 0x12, 0xd4, 0x00,
    0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x01, 0x01,
    0x00, 0x7c, 0x75, 0x83, 0x8b, 0x95, 0xb4, 0xb7, 0xbc, 0x91, 0xeb, 0xa4, 0xd9, 0x70, 0x99, 0x05,
    0x06, 0xfa, 0x07, 0xbe, 0x06, 0xfa, 0x3f, 0xf6, 0xf6, 0xf6, 0x22, 0x52, 0x07, 0xfa, 0x1f, 0xfe,
    0xfe, 0xfe, 0xf1, 0xac, 0x9b, 0xbc, 0x45, 0xfa, 0x0f, 0xad, 0xa7, 0xf8, 0xd0, 0xa6, 0x93, 0xaf,
    0x87, 0x32, 0xf3, 0x27, 0x4a, 0xbd, 0x9e, 0xae, 0x8e, 0x32, 0xa4, 0x9d, 0xf6, 0xbd, 0x8f, 0x76,
    0xaf, 0x2e, 0x30, 0x98, 0x9d, 0x56, 0x16, 0x8f, 0x56, 0x16, 0x30, 0x8e, 0x00, 0xec, 0xf8, 0xd0,
    0xa6, 0x93, 0xa7, 0x8d, 0x32, 0xd9, 0x06, 0xf2, 0x2d, 0x32, 0xbe, 0xf8, 0x01, 0xa7, 0x46, 0xf3,
    0x5c, 0x02, 0xfb, 0x07, 0x32, 0xd2, 0x1c, 0x06, 0xf2, 0x32, 0xce, 0xf8, 0x01, 0xa7, 0x06, 0xf3,
    0x5c, 0x2c, 0x16, 0x8c, 0xfc, 0x08, 0xac, 0x3b, 0xb3, 0xf8, 0xff, 0xa6, 0x87, 0x56, 0x12, 0xd4,
    0x9b, 0xbf, 0xf8, 0xff, 0xaf, 0x93, 0x5f, 0x8f, 0x32, 0xdf, 0x2f, 0x30, 0xe5, 0x00, 0x42, 0xb5,
    0x42, 0xa5, 0xd4, 0x8d, 0xa7, 0x87, 0x32, 0xac, 0x2a, 0x27, 0x30, 0xf5, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x45, 0xa3, 0x98, 0x56, 0xd4, 0xf8, 0x81, 0xbc, 0xf8, 0x95, 0xac,
    0x22, 0xdc, 0x12, 0x56, 0xd4, 0x06, 0xb8, 0xd4, 0x06, 0xa8, 0xd4, 0x64, 0x0a, 0x01, 0xe6, 0x8a,
    0xf4, 0xaa, 0x3b, 0x28, 0x9a, 0xfc, 0x01, 0xba, 0xd4, 0xf8, 0x81, 0xba, 0x06, 0xfa, 0x0f, 0xaa,
    0x0a, 0xaa, 0xd4, 0xe6, 0x06, 0xbf, 0x93, 0xbe, 0xf8, 0x1b, 0xae, 0x2a, 0x1a, 0xf8, 0x00, 0x5a,
    0x0e, 0xf5, 0x3b, 0x4b, 0x56, 0x0a, 0xfc, 0x01, 0x5a, 0x30, 0x40, 0x4e, 0xf6, 0x3b, 0x3c, 0x9f,
    0x56, 0x2a, 0x2a, 0xd4, 0x00, 0x22, 0x86, 0x52, 0xf8, 0xf0, 0xa7, 0x07, 0x5a, 0x87, 0xf3, 0x17,
    0x1a, 0x3a, 0x5b, 0x12, 0xd4, 0x22, 0x86, 0x52, 0xf8, 0xf0, 0xa7, 0x0a, 0x57, 0x87, 0xf3, 0x17,
    0x1a, 0x3a, 0x6b, 0x12, 0xd4, 0x15, 0x85, 0x22, 0x73, 0x95, 0x52, 0x25, 0x45, 0xa5, 0x86, 0xfa,
    0x0f, 0xb5, 0xd4, 0x45, 0xe6, 0xf3, 0x3a, 0x82, 0x15, 0x15, 0xd4, 0x45, 0xe6, 0xf3, 0x3a, 0x88,
    0xd4, 0x45, 0x07, 0x30, 0x8c, 0x45, 0x07, 0x30, 0x84, 0xe6, 0x62, 0x26, 0x45, 0xa3, 0x36, 0x88,
    0xd4, 0x3e, 0x88, 0xd4, 0xf8, 0xf0, 0xa7, 0xe7, 0x45, 0xf4, 0xa5, 0x86, 0xfa, 0x0f, 0x3b, 0xb2,
    0xfc, 0x01, 0xb5, 0xd4, 0x45, 0x56, 0xd4, 0x45, 0xe6, 0xf4, 0x56, 0xd4, 0x45, 0xfa, 0x0f, 0x3a,
    0xc4, 0x07, 0x56, 0xd4, 0xaf, 0x22, 0xf8, 0xd3, 0x73, 0x8f, 0xf9, 0xf0, 0x52, 0xe6, 0x07, 0xd2,
    0x56, 0xf8, 0xff, 0xa6, 0xf8, 0x00, 0x7e, 0x56, 0xd4, 0x19, 0x89, 0xae, 0x93, 0xbe, 0x99, 0xee,
    0xf4, 0x56, 0x76, 0xe6, 0xf4, 0xb9, 0x56, 0x45, 0xf2, 0x56, 0xd4, 0x45, 0xaa, 0x86, 0xfa, 0x0f,
    0xba, 0xd4, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xe0, 0x00, 0x4b
};

/* CHIP-8X interpreter, loaded into RAM at $0000. seems backward-compatible with CHIP-8? */
static const uint8_t chip8x_int[0x300] = {
    0x91, 0xbb, 0xff, 0x01, 0xb2, 0xb6, 0xf8, 0xcf, 0xa2, 0xf8, 0x81, 0xb1, 0xf8, 0x46, 0xa1, 0x90,
    0xb4, 0xf8, 0x1b, 0xa4, 0xf8, 0x02, 0xb5, 0xf8, 0xfa, 0xa5, 0xd4, 0x96, 0xb7, 0xe2, 0x94, 0xbc,
    0x45, 0xaf, 0xf6, 0xf6, 0xf6, 0xf6, 0x32, 0x44, 0xf9, 0x50, 0xac, 0x8f, 0xfa, 0x0f, 0xf9, 0xf0,
    0xa6, 0x05, 0xf6, 0xf6, 0xf6, 0xf6, 0xf9, 0xf0, 0xa7, 0x4c, 0xb3, 0x8c, 0xfc, 0x0f, 0xac, 0x0c,
    0xa3, 0xd3, 0x30, 0x1b, 0x8f, 0xfa, 0x0f, 0xb3, 0x45, 0x30, 0x40, 0x22, 0x69, 0x12, 0xd4, 0x00,
    0x00, 0x01, 0x01, 0x01, 0x01, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x01, 0x00, 0x01, 0x01,
    0x00, 0x7c, 0x75, 0x83, 0x8b, 0xfe, 0xb4, 0xb7, 0xbc, 0x91, 0xeb, 0x00, 0xd9, 0x70, 0x99, 0x05,
    0x06, 0xfa, 0x07, 0xbe, 0x06, 0xfa, 0x3f, 0xf6, 0xf6, 0xf6, 0x22, 0x52, 0x07, 0xfa, 0x1f, 0xfe,
    0xfe, 0xfe, 0xf1, 0xac, 0x9b, 0xbc, 0x45, 0xfa, 0x0f, 0xad, 0xa7, 0xf8, 0xd0, 0xa6, 0x93, 0xaf,
    0x87, 0x32, 0xf3, 0x27, 0x4a, 0xbd, 0x9e, 0xae, 0x8e, 0x32, 0xa4, 0x9d, 0xf6, 0xbd, 0x8f, 0x76,
    0xaf, 0x2e, 0x30, 0x98, 0x9d, 0x56, 0x16, 0x8f, 0x56, 0x16, 0x30, 0x8e, 0x00, 0xec, 0xf8, 0xd0,
    0xa6, 0x93, 0xa7, 0x8d, 0x32, 0xd9, 0x06, 0xf2, 0x2d, 0x32, 0xbe, 0xf8, 0x01, 0xa7, 0x46, 0xf3,
    0x5c, 0x02, 0xfb, 0x07, 0x32, 0xd2, 0x1c, 0x06, 0xf2, 0x32, 0xce, 0xf8, 0x01, 0xa7, 0x06, 0xf3,
    0x5c, 0x2c, 0x16, 0x8c, 0xfc, 0x08, 0xac, 0x3b, 0xb3, 0xf8, 0xff, 0xa6, 0x87, 0x56, 0x12, 0xd4,
    0x9b, 0xbf, 0xf8, 0xff, 0xaf, 0x93, 0x5f, 0x8f, 0x32, 0xdf, 0x2f, 0x30, 0xe5, 0x00, 0x42, 0xb5,
    0x42, 0xa5, 0xd4, 0x8d, 0xa7, 0x87, 0x32, 0xac, 0x2a, 0x27, 0x30, 0xf5, 0x00, 0x00, 0x05, 0xf6,
    0x33, 0xa4, 0x30, 0x95, 0x00, 0x45, 0xa3, 0x98, 0x56, 0xd4, 0xf8, 0x81, 0xbc, 0xf8, 0x95, 0xac,
    0x22, 0xdc, 0x12, 0x56, 0xd4, 0x06, 0xb8, 0xd4, 0x06, 0xa8, 0xd4, 0x64, 0x0a, 0x01, 0xe6, 0x8a,
    0xf4, 0xaa, 0x3b, 0x28, 0x9a, 0xfc, 0x01, 0xba, 0xd4, 0xf8, 0x81, 0xba, 0x06, 0xfa, 0x0f, 0xaa,
    0x0a, 0xaa, 0xd4, 0xe6, 0x06, 0xbf, 0x93, 0xbe, 0xf8, 0x1b, 0xae, 0x2a, 0x1a, 0xf8, 0x00, 0x5a,
    0x0e, 0xf5, 0x3b, 0x4b, 0x56, 0x0a, 0xfc, 0x01, 0x5a, 0x30, 0x40, 0x4e, 0xf6, 0x3b, 0x3c, 0x9f,
    0x56, 0x2a, 0x2a, 0xd4, 0x00, 0x22, 0x86, 0x52, 0xf8, 0xf0, 0xa7, 0x07, 0x5a, 0x87, 0xf3, 0x17,
    0x1a, 0x3a, 0x5b, 0x12, 0xd4, 0x22, 0x86, 0x52, 0xf8, 0xf0, 0xa7, 0x0a, 0x57, 0x87, 0xf3, 0x17,
    0x1a, 0x3a, 0x6b, 0x12, 0xd4, 0x15, 0x85, 0x22, 0x73, 0x95, 0x52, 0x25, 0x45, 0xa5, 0x86, 0xfa,
    0x0f, 0xb5, 0xd4, 0x45, 0xe6, 0xf3, 0x3a, 0x82, 0x15, 0x15, 0xd4, 0x45, 0xe6, 0xf3, 0x3a, 0x88,
    0xd4, 0x45, 0x07, 0x30, 0x8c, 0x45, 0x07, 0x30, 0x84, 0xe6, 0x62, 0x26, 0x45, 0xa3, 0x36, 0x88,
    0xd4, 0x3e, 0x88, 0xd4, 0xe6, 0x06, 0xfa, 0x77, 0x56, 0x07, 0xfa, 0x77, 0xf4, 0xfa, 0x77, 0x56,
    0x15, 0xd4, 0x00, 0x00, 0x45, 0x56, 0xd4, 0x45, 0xe6, 0xf4, 0x56, 0xd4, 0x45, 0xfa, 0x0f, 0x3a,
    0xc4, 0x07, 0x56, 0xd4, 0xaf, 0x22, 0xf8, 0xd3, 0x73, 0x8f, 0xf9, 0xf0, 0x52, 0xe6, 0x07, 0xd2,
    0x56, 0xf8, 0xff, 0xa6, 0xf8, 0x00, 0x7e, 0x56, 0xd4, 0x19, 0x89, 0xae, 0x93, 0xbe, 0x99, 0xee,
    0xf4, 0x56, 0x76, 0xe6, 0xf4, 0xb9, 0x56, 0x45, 0xf2, 0x56, 0xd4, 0x45, 0xaa, 0x86, 0xfa, 0x0f,
    0xba, 0xd4, 0x37, 0x88, 0xd4, 0x3f, 0x88, 0xd4, 0xe6, 0x63, 0xd4, 0xe6, 0x3f, 0xfc, 0x6b, 0xd4,
    0x92, 0xbd, 0xf8, 0x9f, 0xad, 0x0d, 0x32, 0x0a, 0x45, 0xd4, 0x00, 0x45, 0xfa, 0x0f, 0xaf, 0x32,
    0x31, 0x46, 0xfa, 0x3f, 0xf6, 0xf6, 0xf6, 0x22, 0x52, 0xe2, 0x06, 0xfa, 0x1f, 0xfe, 0xfe, 0xfe,
    0xf1, 0xac, 0x12, 0xf8, 0xd0, 0xbc, 0x07, 0x5c, 0x9c, 0xfc, 0x08, 0xac, 0x2f, 0x8f, 0x3a, 0x26,
    0xd4, 0x07, 0xbd, 0x46, 0xac, 0x06, 0xbc, 0x8c, 0xfa, 0x07, 0x22, 0x52, 0xe2, 0xf8, 0xc0, 0xbe,
    0x9c, 0xf6, 0xf6, 0xf6, 0xf6, 0xfa, 0x07, 0xad, 0x9c, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xf4, 0xae,
    0x8c, 0xf6, 0xf6, 0xf6, 0xf6, 0xfa, 0x07, 0xaf, 0x9e, 0xb7, 0x8e, 0xa7, 0x8d, 0xbc, 0xf8, 0x04,
    0xac, 0x9d, 0x57, 0x87, 0xfc, 0x08, 0xa7, 0x2c, 0x8c, 0x3a, 0x61, 0x9c, 0x32, 0x73, 0xff, 0x01,
    0xbc, 0x30, 0x5e, 0x1e, 0x8e, 0xfa, 0xe7, 0xae, 0x8f, 0x32, 0x7e, 0x2f, 0x30, 0x58, 0x12, 0xd4,
    0x92, 0xbd, 0xf8, 0x9f, 0xad, 0xf8, 0xc0, 0xbc, 0x94, 0xac, 0xf8, 0xaa, 0x5c, 0x94, 0xbc, 0xac,
    0x0c, 0xfb, 0x91, 0x32, 0x9c, 0xf8, 0x91, 0x5c, 0xf8, 0x01, 0x5d, 0xd4, 0xf8, 0x00, 0x5d, 0xd4,
    0xe2, 0x65, 0x22, 0xd4, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x80, 0x00, 0xe0, 0x00, 0x4b
};

#if _1802TRACE
static const char *op_mn[0x100] = {
    /*       0        1        2        3        4        5        6        7        8        9        a        b        c        d        e        f */
    /* 0 */ "IDL   ", "LDN R1", "LDN R2", "LDN R3", "LDN R4", "LDN R5", "LDN R6", "LDN R7", "LDN R8", "LDN R9", "LDN RA", "LDN RB", "LDN RC", "LDN RD", "LDN RE", "LDN RF",
    /* 1 */ "INC R0", "INC R1", "INC R2", "INC R3", "INC R4", "INC R5", "INC R6", "INC R7", "INC R8", "INC R9", "INC RA", "INC RB", "INC RC", "INC RD", "INC RE", "INC RF",
    /* 2 */ "DEC 0R", "DEC R1", "DEC R2", "DEC R3", "DEC R4", "DEC R5", "DEC R6", "DEC R7", "DEC R8", "DEC R9", "DEC RA", "DEC RB", "DEC RC", "DEC RD", "DEC RE", "DEC RF",
    /* 3 */ "BR    ", "BQ    ", "BZ    ", "BDF   ", "B1    ", "B2    ", "B3    ", "B4    ", "SKP   ", "BNQ   ", "BNZ   ", "BNF   ", "BN1   ", "BN2   ", "BN3   ", "BN4   ",
    /* 4 */ "LDA R0", "LDA R1", "LDA R2", "LDA R3", "LDA R4", "LDA R5", "LDA R6", "LDA R7", "LDA R8", "LDA R9", "LDA RA", "LDA RB", "LDA RC", "LDA RD", "LDA RE", "LDA RF",
    /* 5 */ "STR R0", "STR R1", "STR R2", "STR R3", "STR R4", "STR R5", "STR R6", "STR R7", "STR R8", "STR R9", "STR RA", "STR RB", "STR RC", "STR RD", "STR RE", "STR RF",
    /* 6 */ "IRX   ", "OUT 1 ", "OUT 2 ", "OUT 3 ", "OUT 4 ", "OUT 5 ", "OUT 6 ", "OUT 7 ", "$68   ", "INP 1 ", "INP 2 ", "INP 3 ", "INP 4 ", "INP 5 ", "INP 6 ", "INP 7 ",
    /* 7 */ "RET   ", "DIS   ", "LDXA  ", "STXD  ", "ADC   ", "SDB   ", "SHRC  ", "SMB   ", "SAV   ", "MARK  ", "REQ   ", "SEQ   ", "ADCI  ", "SDBI  ", "SHLC  ", "SMBI  ",
    /* 8 */ "GLO R0", "GLO R1", "GLO R2", "GLO R3", "GLO R4", "GLO R5", "GLO R6", "GLO R7", "GLO R8", "GLO R9", "GLO RA", "GLO RB", "GLO RC", "GLO RD", "GLO RE", "GLO RF",
    /* 9 */ "GHI R0", "GHI R1", "GHI R2", "GHI R3", "GHI R4", "GHI R5", "GHI R6", "GHI R7", "GHI R8", "GHI R9", "GHI RA", "GHI RB", "GHI RC", "GHI RD", "GHI RE", "GHI RF",
    /* a */ "PLO R0", "PLO R1", "PLO R2", "PLO R3", "PLO R4", "PLO R5", "PLO R6", "PLO R7", "PLO R8", "PLO R9", "PLO RA", "PLO RB", "PLO RC", "PLO RD", "PLO RE", "PLO RF",
    /* b */ "PHI R0", "PHI R1", "PHI R2", "PHI R3", "PHI R4", "PHI R5", "PHI R6", "PHI R7", "PHI R8", "PHI R9", "PHI RA", "PHI RB", "PHI RC", "PHI RD", "PHI RE", "PHI RF",
    /* c */ "LBR   ", "LBQ   ", "LBZ   ", "LBDF  ", "NOP   ", "LSNQ  ", "LSNZ  ", "LSNF  ", "LSKP  ", "LBNQ  ", "LBNZ  ", "LBNF  ", "LSIE  ", "LSQ   ", "LSZ   ", "LSDF  ",
    /* d */ "SEP R0", "SEP R1", "SEP R2", "SEP R3", "SEP R4", "SEP R5", "SEP R6", "SEP R7", "SEP R8", "SEP R9", "SEP RA", "SEP RB", "SEP RC", "SEP RD", "SEP RE", "SEP RF",
    /* e */ "SEX R0", "SEX R1", "SEX R2", "SEX R3", "SEX R4", "SEX R5", "SEX R6", "SEX R7", "SEX R8", "SEX R9", "SEX RA", "SEX RB", "SEX RC", "SEX RD", "SEX RE", "SEX RF",
    /* f */ "LDX   ", "OR    ", "AND   ", "XOR   ", "ADD   ", "SD    ", "SHR   ", "SM    ", "LDI   ", "ORI   ", "ANI   ", "XRI   ", "ADI   ", "SDI   ", "SHL   ", "SMI   "
};
#endif //_1802TRACE

/*-----------------------------------*/

static struct {
    /* 1802 cpu */
    uint16_t r[16];   /* scratchpad */
    int ef[4];      /* io lines */
    int df;         /* data flag */
    uint8_t d;    /* data (accumulator) */
    int p;          /* programcounter pointer (nybble) */
    uint8_t b;    /* auxiliary, unused(?) */
    int x;          /* data pointer (nybble) */
    int t;          /* hold interrupt x/p (byte) */
    int ie;         /* interrupt enable flag */
    int q;          /* output flip-flop */

    /* rest */
    int pixie_on;       /* graphics are on/off */
    int key;        /* hex key latch */
    int idle;       /* opcode 0 */
    int stopped;        /* infinite idle */
    int cycles;     /* machine cycles */
} vip;

#define D vip.d
#define DF vip.df
#define B vip.b
#define P vip.p
#define X vip.x
#define T vip.t
#define IE vip.ie
#define Q vip.q
#define R vip.r
#define EF vip.ef
#define EF1 vip.ef[0]
#define EF2 vip.ef[1]
#define EF3 vip.ef[2]
#define EF4 vip.ef[3]
#define N (opcode&0xf) /* also a cpu reg actually */
#define RP R[P]
#define RX R[X]
#define RN R[N]

#define R8(a)   (((a) & 0x8000) ? vip_os[(a) & 0x1ff] : ram[(a) & 0xfff])
#define W8(a,v) if (!((a) & 0x8000)) ram[(a) & 0xfff] = v

static uint8_t *ram = NULL;

/*-----------------------------------*/

int vip_stopped(void)
{
    return vip.stopped;
}

void vip_reset(void)
{
    vip.idle = Q = X = P = 0;
    RP = 0;
    IE = 1;
    R[1] = 0xfff; /* 4k ram */

    /* simulate OS start routine (so I don't have to emulate the address latch): */
    /*
        LDI $80
        PHI R2
        LDI $08
        PLO R2
        SEX R2
        SEP R2
    */
    D = 8;
    X = P = 2;
    RP = 0x8008;
    vip.cycles = -12; /* actually, the reset itself also eats up a few clockcycles, but that doesn't need to be emulated */
}

void vip_init(void)
{
    /* $100 bytes padding in case switching to chip8x with a (too) big opened file */
    ram = shell_get_ram_ptr(0x1100);
    memset(ram, 0, 0x1000);

#if USE_SAVE_STATE
    memset(state, 0, STATE_MAX);
#endif //USE_SAVE_STATE

    memset(&vip, 0, sizeof(vip));

    R[0xb] = 0xf00; /* set default vram offset */
}

void vip_chip8_hardreset(void)
{
    uint8_t *buffer = file_get_buffer();

    vip_init();

    if (file_is_open()) {
        int sa = file_get_sa();
        if (file_get_sa_default()) {
            sa = 0x200;
        }
        if (sa >= 0x200) {
            memcpy(ram, chip8_int, 0x200);    /* load chip8 interpreter */
        }
        memcpy(ram + sa, buffer, file_get_size()); /* load program */
    }

    vip_reset();
}

void vip_chip8x_hardreset(void)
{
    uint8_t *buffer = file_get_buffer();

    vip_init();

    if (file_is_open()) {
        int sa = file_get_sa();
        if (file_get_sa_default()) {
            sa = 0x300;
        }
        if (sa >= 0x300) {
            memcpy(ram, chip8x_int, 0x300);    /* load chip8x interpreter */
        }
        memcpy(ram + sa, buffer, file_get_size()); /* load program */
    }

    vip_reset();
}

static void vip_execute(int border)
{
    int opcode;

    if (vip.idle) {
        vip.cycles = border;
    }

    while (vip.cycles > border) {
        /* EF2=tape-in, unemulated/unnecessary, just use File->Open */
        EF3 = input_is_pressed(vip.key);
        opcode = R8(RP);
        RP++;
        vip.cycles -= 2;

#if _1802TRACE
        printf("%04X: %s %02X %02X -- D:%02X DF:%d P:%X X:%X Q:%d -- R0:%04X R1:%04X R2:%04X R3:%04X R4:%04X R5:%04X R6:%04X R7:%04X R8:%04X R9:%04X RA:%04X RB:%04X RC:%04X RD:%04X RE:%04X RF:%04X\n", RP - 1, op_mn[opcode], R8(RP), R8(RP + 1), D, DF, P, X, Q, R[0], R[1], R[2], R[3], R[4], R[5], R[6], R[7], R[8], R[9], R[10], R[11], R[12], R[13], R[14], R[15]);
#endif //_1802TRACE

        switch (opcode) {
            /* memory reference */
        /* no 0 */ case 0x01:
        case 0x02:
        case 0x03:
        case 0x04:
        case 0x05:
        case 0x06:
        case 0x07:
        case 0x08:
        case 0x09:
        case 0x0a:
        case 0x0b:
        case 0x0c:
        case 0x0d:
        case 0x0e:
        case 0x0f:
            D = R8(RN);
            break;                        /* LDN Rn */
        case 0x40:
        case 0x41:
        case 0x42:
        case 0x43:
        case 0x44:
        case 0x45:
        case 0x46:
        case 0x47:
        case 0x48:
        case 0x49:
        case 0x4a:
        case 0x4b:
        case 0x4c:
        case 0x4d:
        case 0x4e:
        case 0x4f:
            D = R8(RN);
            RN++;
            break;                      /* LDA Rn */
        case 0xf0:
            D = R8(RX);
            break;                     /* LDX */
        case 0x72:
            D = R8(RX);
            RX++;
            break;                   /* LDXA */
        case 0xf8:
            D = R8(RP);
            RP++;
            break;                   /* LDI */
        case 0x50:
        case 0x51:
        case 0x52:
        case 0x53:
        case 0x54:
        case 0x55:
        case 0x56:
        case 0x57:
        case 0x58:
        case 0x59:
        case 0x5a:
        case 0x5b:
        case 0x5c:
        case 0x5d:
        case 0x5e:
        case 0x5f:
            W8(RN, D);
            break;                        /* STR Rn */
        case 0x73:
            W8(RX, D);
            RX--;
            break;                   /* STXD */

            /* register operations */
        case 0x10:
        case 0x11:
        case 0x12:
        case 0x13:
        case 0x14:
        case 0x15:
        case 0x16:
        case 0x17:
        case 0x18:
        case 0x19:
        case 0x1a:
        case 0x1b:
        case 0x1c:
        case 0x1d:
        case 0x1e:
        case 0x1f:
            RN++;
            break;                            /* INC Rn */
        case 0x20:
        case 0x21:
        case 0x22:
        case 0x23:
        case 0x24:
        case 0x25:
        case 0x26:
        case 0x27:
        case 0x28:
        case 0x29:
        case 0x2a:
        case 0x2b:
        case 0x2c:
        case 0x2d:
        case 0x2e:
        case 0x2f:
            RN--;
            break;                            /* DEC Rn */
        case 0x60:
            RX++;
            break;                         /* IRX */
        case 0x80:
        case 0x81:
        case 0x82:
        case 0x83:
        case 0x84:
        case 0x85:
        case 0x86:
        case 0x87:
        case 0x88:
        case 0x89:
        case 0x8a:
        case 0x8b:
        case 0x8c:
        case 0x8d:
        case 0x8e:
        case 0x8f:
            D = RN & 0xff;
            break;                       /* GLO Rn */
        case 0xa0:
        case 0xa1:
        case 0xa2:
        case 0xa3:
        case 0xa4:
        case 0xa5:
        case 0xa6:
        case 0xa7:
        case 0xa8:
        case 0xa9:
        case 0xaa:
        case 0xab:
        case 0xac:
        case 0xad:
        case 0xae:
        case 0xaf:
            RN = (RN & 0xff00) | D;
            break;                    /* PLO Rn */
        case 0x90:
        case 0x91:
        case 0x92:
        case 0x93:
        case 0x94:
        case 0x95:
        case 0x96:
        case 0x97:
        case 0x98:
        case 0x99:
        case 0x9a:
        case 0x9b:
        case 0x9c:
        case 0x9d:
        case 0x9e:
        case 0x9f:
            D = RN >> 8;
            break;                         /* GHI Rn */
        case 0xb0:
        case 0xb1:
        case 0xb2:
        case 0xb3:
        case 0xb4:
        case 0xb5:
        case 0xb6:
        case 0xb7:
        case 0xb8:
        case 0xb9:
        case 0xba:
        case 0xbb:
        case 0xbc:
        case 0xbd:
        case 0xbe:
        case 0xbf:
            RN = (RN & 0xff) | (D << 8);
            break;                 /* PHI Rn */

            /* logic operations */
        case 0xf1:
            D |= R8(RX);
            break;                        /* OR */
        case 0xf9:
            D |= R8(RP);
            RP++;
            break;                  /* ORI */
        case 0xf3:
            D ^= R8(RX);
            break;                        /* XOR */
        case 0xfb:
            D ^= R8(RP);
            RP++;
            break;                  /* XRI */
        case 0xf2:
            D &= R8(RX);
            break;                        /* AND */
        case 0xfa:
            D &= R8(RP);
            RP++;
            break;                  /* ANI */
        case 0xf6:
            DF = D & 1;
            D >>= 1;
            break;                    /* SHR */
        case 0x76: {
            int c = DF << 7;    /* SHRC */
            DF = D & 1;
            D = D >> 1 | c;
            break;
        }
        case 0xfe:
            DF = D >> 7;
            D <<= 1;
            break;                   /* SHL */
        case 0x7e: {
            int c = DF;    /* SHLC */
            DF = D >> 7;
            D = D << 1 | c;
            break;
        }

        /* arithmetic operations */
        case 0xf4: {
            int a = R8(RX) + D;    /* ADD */
            DF = a >> 8;
            D = a;
            break;
        }
        case 0xfc: {
            int a = R8(RP) + D;    /* ADI */
            RP++;
            DF = a >> 8;
            D = a;
            break;
        }
        case 0x74: {
            int a = R8(RX) + D + DF;    /* ADC */
            DF = a >> 8;
            D = a;
            break;
        }
        case 0x7c: {
            int a = R8(RP) + D + DF;    /* ADCI */
            RP++;
            DF = a >> 8;
            D = a;
            break;
        }
        case 0xf5: {
            int a = R8(RX) + (D ^ 0xff) + 1;    /* SD */
            DF = a >> 8;
            D = a;
            break;
        }
        case 0xfd: {
            int a = R8(RP) + (D ^ 0xff) + 1;    /* SDI */
            RP++;
            DF = a >> 8;
            D = a;
            break;
        }
        case 0x75: {
            int a = R8(RX) + (D ^ 0xff) + DF;    /* SDB */
            DF = a >> 8;
            D = a;
            break;
        }
        case 0x7d: {
            int a = R8(RP) + (D ^ 0xff) + DF;    /* SDBI */
            RP++;
            DF = a >> 8;
            D = a;
            break;
        }
        case 0xf7: {
            int a = (R8(RX) ^ 0xff) + D + 1;    /* SM */
            DF = a >> 8;
            D = a;
            break;
        }
        case 0xff: {
            int a = (R8(RP) ^ 0xff) + D + 1;    /* SMI */
            RP++;
            DF = a >> 8;
            D = a;
            break;
        }
        case 0x77: {
            int a = (R8(RX) ^ 0xff) + D + DF;    /* SMB */
            DF = a >> 8;
            D = a;
            break;
        }
        case 0x7f: {
            int a = (R8(RP) ^ 0xff) + D + DF;    /* SMBI */
            RP++;
            DF = a >> 8;
            D = a;
            break;
        }

        /* branch instructions - short branch */
#define SB() RP=(RP&0xff00)|R8(RP)
        case 0x30:
            SB();
            break;                         /* BR */
        case 0x32:
            if (!D)   {
                SB();
            } else {
                RP++;
            }
            break;            /* BZ */
        case 0x3a:
            if (D)    {
                SB();
            } else {
                RP++;
            }
            break;            /* BNZ */
        case 0x33:
            if (DF)   {
                SB();
            } else {
                RP++;
            }
            break;            /* BDF */
        case 0x3b:
            if (!DF)  {
                SB();
            } else {
                RP++;
            }
            break;            /* BNF */
        case 0x31:
            if (Q)    {
                SB();
            } else {
                RP++;
            }
            break;            /* BQ */
        case 0x39:
            if (!Q)   {
                SB();
            } else {
                RP++;
            }
            break;            /* BNQ */
        case 0x34:
            if (EF1)  {
                SB();
            } else {
                RP++;
            }
            break;            /* B1 */
        case 0x3c:
            if (!EF1) {
                SB();
            } else {
                RP++;
            }
            break;            /* BN1 */
        case 0x35:
            if (EF2)  {
                SB();
            } else {
                RP++;
            }
            break;            /* B2 */
        case 0x3d:
            if (!EF2) {
                SB();
            } else {
                RP++;
            }
            break;            /* BN2 */
        case 0x36:
            if (EF3)  {
                SB();
            } else {
                RP++;
            }
            break;            /* B3 */
        case 0x3e:
            if (!EF3) {
                SB();
            } else {
                RP++;
            }
            break;            /* BN3 */
        case 0x37:
            if (EF4)  {
                SB();
            } else {
                RP++;
            }
            break;            /* B4 */
        case 0x3f:
            if (!EF4) {
                SB();
            } else {
                RP++;
            }
            break;            /* BN4 */

            /* branch instructions - long branch */
#define LB() RP=(R8(RP)<<8)|R8(RP+1)
        case 0xc0:
            LB();
            break;                         /* LBR */
        case 0xc2:
            if (!D)   {
                LB();
            } else {
                RP += 2;
            }
            vip.cycles--;
            break;     /* LBZ */
        case 0xca:
            if (D)    {
                LB();
            } else {
                RP += 2;
            }
            vip.cycles--;
            break;     /* LBNZ */
        case 0xc3:
            if (DF)   {
                LB();
            } else {
                RP += 2;
            }
            vip.cycles--;
            break;     /* LBDF */
        case 0xcb:
            if (!DF)  {
                LB();
            } else {
                RP += 2;
            }
            vip.cycles--;
            break;     /* LBNF */
        case 0xc1:
            if (Q)    {
                LB();
            } else {
                RP += 2;
            }
            vip.cycles--;
            break;     /* LBQ */
        case 0xc9:
            if (!Q)   {
                LB();
            } else {
                RP += 2;
            }
            vip.cycles--;
            break;     /* LBNQ */

            /* skip instructions */
        case 0x38:              /* SKP */
            RP++;
            break;
        case 0xc8:              /* LSKP */
            RP += 2;
            vip.cycles--;
            break;
        case 0xce:              /* LSZ */
            if (!D) {
                RP += 2;
            }
            vip.cycles--;
            break;
        case 0xc6:              /* LSNZ */
            if (D) {
                RP += 2;
            }
            vip.cycles--;
            break;
        case 0xcf:              /* LSDF */
            if (DF) {
                RP += 2;
            }
            vip.cycles--;
            break;
        case 0xc7:              /* LSNF */
            if (!DF) {
                RP += 2;
            }
            vip.cycles--;
            break;
        case 0xcd:              /* LSQ */
            if (Q) {
                RP += 2;
            }
            vip.cycles--;
            break;
        case 0xc5:              /* LSNQ */
            if (!Q) {
                RP += 2;
            }
            vip.cycles--;
            break;
        case 0xcc:              /* LSIE */
            if (IE) {
                RP += 2;
            }
            vip.cycles--;
            break;

            /* control instructions */
        case 0x00:              /* IDL */
            vip.idle = 1;
            vip.cycles = border;
            break;
        case 0xc4:              /* NOP */
            vip.cycles--;
            break;
        case 0xd0:
        case 0xd1:
        case 0xd2:
        case 0xd3:
        case 0xd4:
        case 0xd5:
        case 0xd6:
        case 0xd7:
        case 0xd8:
        case 0xd9:
        case 0xda:
        case 0xdb:
        case 0xdc:
        case 0xdd:
        case 0xde:
        case 0xdf:              /* SEP Rx */
            P = N;
            break;
        case 0xe0:
        case 0xe1:
        case 0xe2:
        case 0xe3:
        case 0xe4:
        case 0xe5:
        case 0xe6:
        case 0xe7:
        case 0xe8:
        case 0xe9:
        case 0xea:
        case 0xeb:
        case 0xec:
        case 0xed:
        case 0xee:
        case 0xef:              /* SEX Rx */
            X = N;
            break;
        case 0x7b:              /* SEQ */
            Q = 1;
            break;
        case 0x7a:              /* REQ */
            Q = 0;
            break;
        case 0x78:              /* SAV */
            W8(RX, T);
            break;
        case 0x79:              /* MARK */
            T = X << 4 | P;
            X = P;
            W8(R[2], T);
            R[2]--;
            break;
        case 0x70: {            /* RET */
            int r = R8(RX);
            RX++;
            P = r & 0xf;
            X = r >> 4;
            IE = 1;
            break;
        }
        case 0x71: {            /* DIS */
            int r = R8(RX);
            RX++;
            P = r & 0xf;
            X = r >> 4;
            IE = 0;
            break;
        }
        
        /* input - output byte transfer */
        case 0x61:
        case 0x62:
        case 0x63:
        case 0x64:
        case 0x65:
        case 0x66:
        case 0x67: {                /* OUT 1/7 */
            int r = R8(RX);
            RX++;                     
            switch (opcode & 7) {
            case 1:                 /* pixie graphics off */
                vip.pixie_on = 0;
                break;
            case 2:                 /* hex keypad latch */
                vip.key = r & 0xf;
                break;
            case 4:                 /* reset address latch (unneeded) */
                break;
            default:
                break;
            }
            break;
        }
        case 0x69:
        case 0x6a:
        case 0x6b:
        case 0x6c:
        case 0x6d:
        case 0x6e:
        case 0x6f: {                /* INP 1/7 */
            int r = opcode;
            switch (opcode & 7) {
            case 1:
                vip.pixie_on = 1;   /* pixie graphics on */
                break;
            default:
                break;
            }
            D = r;
            W8(RX, r);
            break;
        }
        case 0x68:                  /* the only 'illegal' opcode */
            break;
        }
    } /* big loop */
}

void vip_frame(void)
{
    int i;
    uint8_t *scr = shell_get_vram_ptr(0x400);

    shell_scr_flush();

    /* 1861 (Pixie): 262 scanlines total, 14 machine cycles per scanline */
    vip.cycles += (262 * 14);

    /* start in blank area (doesn't matter where to start, frame is circular) */
    vip_execute(132 * 14); /* post display 54 blank scanlines+16 vblank scanlines+pre display 60 blank scanlines */

    /* 4 scanlines before display update */
    EF1 = 0;
    vip_execute(130 * 14 + 2);

    /* 2 scanlines+2 cycles before display update, irqline enabled for 28 cycles */
    i = 28;
    while (i--) {
        if (IE & vip.pixie_on) {
            /* Pixie generates interrupt */
#if _1802TRACE
            printf("_INTERRUPT\n");
#endif //_1802TRACE
            T = X << 4 | P;
            X = 2;
            P = 1;
            IE = vip.idle = 0;

            vip.cycles--;
            i--;
            if (i < 0) {
                break;
            }
        }
        vip_execute(128 * 14 + 2 + i);
    }
    vip.stopped = vip.idle;

    /* continue to display area */
    vip_execute(128 * 14);

    /* display update */
    if (vip.pixie_on) {
        /* update screen, 1024 stolen DMA cycles for drawing, 768 for horizontal retrace */
        i = 128;
#if _1802TRACE
        printf("_DRAW START\n");
#endif //_1802TRACE
        while (i--) {
            /* 128 scanlines, 8 cycles DMA out, 6 cycles retrace */
#if _1802TRACE
            printf("_LINE %d\n", 128 - i);
#endif //_1802TRACE
            *scr++ = R8(R[0]);
            R[0]++;
            *scr++ = R8(R[0]);
            R[0]++;
            *scr++ = R8(R[0]);
            R[0]++;
            *scr++ = R8(R[0]);
            R[0]++;
            *scr++ = R8(R[0]);
            R[0]++;
            *scr++ = R8(R[0]);
            R[0]++;
            *scr++ = R8(R[0]);
            R[0]++;
            *scr++ = R8(R[0]);
            R[0]++;

            vip.cycles -= 8;
            vip_execute(i * 14);

            if (i == 4) {
                EF1 = 1;    /* 4 scanlines before display end */
            }
        }
#if _1802TRACE
        printf("_DRAW END\n");
#endif //_1802TRACE
    } else {
        /* no dma */
        memset(scr, 0, 0x400);
        vip_execute(4 * 14);
        EF1 = 1;
        vip_execute(0);
    }
    /* end of frame */

    /* actually, Q is wired to a (astable) CA555 timer circuit using some discrete components, which is wired to the speaker. */
    if (Q) {
        sound_play();
    } else {
        sound_stop();
    }
    /* Q is also wired to tape_out, which I'll leave unemulated. */
}

#if USE_SAVE_STATE
/* save state/load state */
void vip_save_state(void)
{
    state[STATE_MAX - 1] = 1; /* set save state flag */

    memcpy(state, ram, 0x1000);     /* ram */
    memcpy(state + 0x1000, &vip, sizeof(vip)); /* data */
}

void vip_load_state(void)
{
    if (state[STATE_MAX - 1] == 0) {
        return;
    }

    memcpy(ram, state, 0x1000);     /* ram */
    memcpy(&vip, state + 0x1000, sizeof(vip)); /* data */
}
#endif //USE_SAVE_STATE
